package com.example.ole.taskqueue.Q.base;

import android.os.ConditionVariable;

import com.example.ole.taskqueue.Print;

import java.util.ArrayList;

public abstract class XTaskAbs extends PrioritizedRunnable {
    public static final int RET_NORMAL = 0x00;
    public static final int RET_PAUSED = 0x01;
    public static final int RET_TERMINATED = 0x02;
    public String TAG;
    private static long sTaskNumber;
    private final int mEventType;
    private final long mTaskNumber;
    private XProgressController mProgressCtrl;

    private int mResult;
    private int pc;
    private int pcTerminatePos = 0xffffffff;
    private ArrayList<Instruction> instructions;
    private volatile boolean eop;
    private ConditionVariable mConditionFinished;

    public XTaskAbs(int eventType) {
        super(eventType, ++sTaskNumber);
        instructions = new ArrayList<>(100); // Manual change this to a fixed number. When you know how many instructions your program have.
        pc = 0;
        eop = false;

        mResult = RET_NORMAL;
        mEventType = eventType;
        mTaskNumber = sTaskNumber;
        mConditionFinished = new ConditionVariable(false);
        TAG = String.format("RecTask-%04d-%d", mTaskNumber, mEventType);
        compileProgram(instructions);
    }

    public void compileProgram(ArrayList<Instruction> instructions) {
        instructions.trimToSize();
        Print.d(1, 1, TAG, "Compile finished, Program has " + instructions.size() + " instructions.");
    }

    /**
     * @param waitUntilFinish
     * @param timeoutMs
     * @return false on timeout.
     */
    public boolean waitForRecordingFinished(boolean waitUntilFinish, int timeoutMs) {
        if (waitUntilFinish) {
            return mConditionFinished.block(timeoutMs);
        }
        return true;
    }

    public void markCurrentAsBeginningOfTerminateAddress() {
        pcTerminatePos = instructions.size();
    }

    public int getEventType() {
        return mEventType;
    }

    @Override
    public void run() {
        mResult = RET_NORMAL;
        mConditionFinished.close();
        while (isRunning() && !eop) {
            eop = pcToNext();
        }
        mConditionFinished.open();
    }

    private boolean pcToNext() {
        if (pc >= instructions.size()) {
            return true; // end of program.
        } else {
            instructions.get(pc).exec(this);
            pc++;
            return false;
        }
    }

    /**
     * @param isAbsolutePosition absolute position or relative position.
     * @param position
     */
    public void pcGoto(boolean isAbsolutePosition, int position) {
        // TODO Lock is expensive, try atomic?
        if (isAbsolutePosition) {
            pc = position;
        } else {
            if (pc + position < 0) {
                pc = 0;
            } else if (pc + position > instructions.size() - 1) {
                pc = instructions.size() - 1;
            } else {
                pc = pc + position;
            }
        }
    }

    public void setProgressController(XProgressController progressCtrl) {
        mProgressCtrl = progressCtrl;
    }

    public boolean isRunning() {
        if (mProgressCtrl == null) {
            return true;
        } else if (mProgressCtrl.getRecordingStatus() == RET_PAUSED) {
            mResult = RET_PAUSED;
            return false;
        } else if (mProgressCtrl.getRecordingStatus() == RET_TERMINATED) {
            if (mResult != RET_TERMINATED) {
                pcGoto(true, pcTerminatePos);
            }
            mResult = RET_TERMINATED;
            return true;
        } else {
            return true;
        }
    }

    public int getTaskResult() {
        return mResult;
    }

    public String identity() {
        return String.format("{No.%04d Type:%d}", mTaskNumber, mEventType);
    }
}
